package com.zhihuishu.micro.course.service.impl;

import com.zhihuishu.micro.course.dto.TreenityDto;
import com.zhihuishu.micro.course.service.TreenityRedisService;
import com.zhihuishu.toolkit.helper.BeanHelper;
import com.zhihuishu.toolkit.jedis.JedisHelper;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

import java.util.*;

/**
 * @author Jiangli
 * @date 2016/10/20 9:33
 *
 * @see TreenityRedisService
 */
public abstract class BaseTreenityServiceRedisImpl<DTO extends TreenityDto> extends  BaseService implements TreenityRedisService<DTO> {
    protected abstract Class<DTO> getDTOClass();

    /**
     * 对象id-对象 redis key一般模式如下
     * --> xx:id->{propA:xx,propB:xx} (hash)
     *
     *  实际调用
     ** {@link Jedis#expire(String, int)}
     *
     * 能刷新对象超时时间的地方
     * @see BaseTreenityServiceImpl#list(List<Integer>) 涉及到db查询时
     * @see BaseTreenityServiceImpl#detail(Integer) db为空时
     * @see BaseTreenityServiceImpl#create(DTO, long) 总是
     *
     * @see #updateObjectCache
     *
     */
    protected  Integer objectKeyExpire(){
        return null;//默认永不超时
    }
    /**
     * onToMany-对象 redis key的超时时间
     * --> xx:onToManyId->id1,id2,id3 (set)
     *
     * 实际调用
     ** {@link Jedis#expire(String, int)}
     *
     *  能刷新对象超时时间的地方
     * @see BaseTreenityServiceImpl#listDtoIds(long)  涉及到db查询时
     * @see BaseTreenityServiceImpl#create(DTO, long) 总是
     *
     * @see #updateIdListCache
     * @see #addIdInListCache
     */
    protected  Integer idsKeyExpire(){
        return null;//默认永不超时
    }

    @Override
    public DTO detail(final Integer dtoId) {
        final List<DTO> dtoObjects = new ArrayList<DTO>();
        jedisExecutor.execute( new JedisHelper.Handler() {
            @Override
            public void handle(Jedis jedis) {
                if (dtoId != null) {
                    DTO dto = oneFromRedis(jedis, dtoId);
                    dtoObjects.add(dto);
                }
            }
        });

        if (!CollectionUtils.isEmpty(dtoObjects)) {
            return dtoObjects.get(0);
        }
        return null;
    }

    @Override
    public void update(DTO dto, long updateUserId) {
        updateObjectCache(dto);
    }

    @Override
    public void deleteRelationShip(final long oneToManyId, final Integer dtoId) {
        jedisExecutor.execute( new JedisHelper.Handler() {
            @Override
            public void handle(Jedis jedis) {
                doDeleteRelationShip(jedis, oneToManyId, dtoId);
            }
        });
    }

    protected void doDeleteRelationShip(Jedis jedis, long oneToManyId, Integer dtoId) {
        String idsKey = getIdsKey(oneToManyId);

        jedis.srem(idsKey,String.valueOf(dtoId));

        log.debug("removed id in course-dto relationship. dtoId:{}, oneToManyId:{}",dtoId,oneToManyId);
    }

    @Override
    public void delete(final Integer dtoId, long deleteUserId) {
        jedisExecutor.execute( new JedisHelper.Handler() {
            @Override
            public void handle(Jedis jedis) {
                doDeleteDto(jedis, dtoId);
            }
        });
    }

    protected void doDeleteDto(Jedis jedis, Integer dtoId) {
        //remove obj key
        String objectKey = getObjectKey(dtoId);
        Long del = jedis.del(objectKey);
    }
    
   /* @Override
    public void sort(final List<DTO> dbDtoList, long updateUserId) {
        jedisExecutor.execute( new JedisHelper.Handler() {
            @Override
            public void handle(Jedis jedis) {
                for (DTO dto : dbDtoList) {
                    updateObjectCacheOfkey(jedis, dto.getId(), "rank", String.valueOf(dto.getRank()));
                }
            }
        });
    }*/


    @Override
    public void updateObjectListCache(final List<DTO> list) {
        jedisExecutor.execute( new JedisHelper.Handler() {
            @Override
            public void handle(Jedis jedis) {
                for (DTO dto : list) {
                    updateObjectCache(jedis, dto);
                }
            }
        });

    }

    protected void updateObjectCache(Jedis jedis, DTO dto) {
        if (dto != null && dto.getId() != null) {
            String key = getObjectKey(dto.getId());

            jedis.del(key);
            jedis.hmset(key, BeanHelper.describe(dto));

            expireKey(jedis, key, objectKeyExpire());
        }
    }

    private void expireKey(Jedis jedis, String key, Integer expireSecond) {
        Integer expire = expireSecond;
        if (expire != null && expire >0) {
             log.debug("set expire time for key:{} time:{}",key,expireSecond);
            jedis.expire(key, expire);
        } else {
            log.debug("do not set expire time ... key:{}",key);
        }
    }

    protected void updateObjectCacheOfkey(Jedis jedis, Integer dtoId, String prop, String value) {
        String key = getObjectKey(dtoId);

        jedis.hdel(key, prop);
        jedis.hset(key, prop, value);
    }

    @Override
    public void updateObjectCache(final DTO dto) {
        jedisExecutor.execute( new JedisHelper.Handler() {
            @Override
            public void handle(Jedis jedis) {
                updateObjectCache(jedis, dto);
            }
        });
    }

    @Override
    public void updateIdListCache(final long oneToManyId, final List<Integer> listChapterIds) {
        if(!CollectionUtils.isEmpty(listChapterIds)){
            jedisExecutor.execute( new JedisHelper.Handler() {
                @Override
                public void handle(Jedis jedis) {
                    String key = getIdsKey(oneToManyId);

                    jedis.del(key);
                    Set<String> dtoIds = new HashSet<String>();
                    for (Integer listChapterId : listChapterIds) {
                        dtoIds.add(String.valueOf(listChapterId));
                    }
                    jedis.sadd(key, dtoIds.toArray(new String[dtoIds.size()]));

                    expireKey(jedis, key, idsKeyExpire());
                }
            });
        }
    }

    @Override
	public void addIdInListCache(final long oneToManyId, final Integer newChapterId) {
        jedisExecutor.execute( new JedisHelper.Handler() {
            @Override
            public void handle(Jedis jedis) {
                doAddIdInListCache(jedis, oneToManyId, newChapterId);
            }
        });
    }

    protected void doAddIdInListCache(Jedis jedis, long oneToManyId, Integer newChapterId) {
        String key = getIdsKey(oneToManyId);

        jedis.sadd(key, String.valueOf(newChapterId));

        expireKey(jedis, key, idsKeyExpire());
    }

    @Override
    public Integer create(final DTO dto, long creator) {
        final Integer dtoId = dto.getId();
        Long oneToManyId = dto.getOneToManyId();

        if (dtoId != null) {
            updateObjectCache(dto);
            if (oneToManyId != null) {
                addIdInListCache(oneToManyId, dtoId);
            }
        }

        return dtoId;
    }



    @Override
    public List<DTO> list(final long oneToManyId) {
        List<Integer> listChapterIds = listDtoIds(oneToManyId);
        List<DTO> list = list(listChapterIds);
        return list;
    }

    @Override
    public List<Integer> listDtoIds(final long oneToManyId) {
        final List<Integer> dtoIds = new ArrayList<Integer>();
        jedisExecutor.execute( new JedisHelper.Handler() {
            @Override
            public void handle(Jedis jedis) {
                String key = getIdsKey(oneToManyId);
                Set<String> sets = jedis.smembers(key);
                for (String set : sets) {
                    try {
                        dtoIds.add(Integer.parseInt(set));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        return dtoIds;
    }


    @Override
    public List<DTO> list(final List<Integer> listChapterIds) {
        final List<DTO> dtoObjects = new ArrayList<DTO>();
        jedisExecutor.execute( new JedisHelper.Handler() {
            @Override
            public void handle(Jedis jedis) {
                List<DTO> listFromRedis = listFromRedis(jedis, listChapterIds);
                dtoObjects.addAll(listFromRedis);
            }
        });
        return dtoObjects;
    }

    protected DTO oneFromRedis(Jedis jedis, Integer dtoId) {
        List<DTO> dtos = listFromRedis(jedis, Arrays.asList(dtoId));

        if (!CollectionUtils.isEmpty(dtos)) {
            return dtos.get(0);
        }
        return null;
    }

    protected List<DTO> listFromRedis(Jedis jedis, List<Integer> dtoIds) {
        List<DTO> dtoObjects = new ArrayList<DTO>();
        Pipeline p = jedis.pipelined();
        // 使用管道，根据章ID列表，从Redis中查询章信息
        for (Integer dtoId : dtoIds) {
            p.hgetAll(getObjectKey(dtoId));
        }
        List<Object> list = p.syncAndReturnAll();
        if (!CollectionUtils.isEmpty(list)) {
            for (Object obj : list) {
                if (obj == null) {
                    continue;
                }
                Map<String, String> hash = (Map<String, String>) obj;
                if (CollectionUtils.isEmpty(hash) || hash.get("id") == null) {
                    continue;
                }
                dtoObjects.add(BeanHelper.transTo(hash, getDTOClass()));
            }
        }
        return dtoObjects;
    }
}
